﻿using Colorful.Furniture.BasicDataManagement;
using Colorful.Furniture.CommonDto;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;

namespace Colorful.Furniture.CommonManagement;

public class FileAppService : ApplicationService
{
    private readonly ILogger<FileAppService> _logger;
    private readonly IHttpContextAccessor _contextAccessor;
    private readonly FileConfig _fileConfig;

    public FileAppService(ILogger<FileAppService> logger,
        IOptions<FileConfig> fileConfig,
        IHttpContextAccessor contextAccessor)
    {
        _logger = logger;
        _contextAccessor = contextAccessor;
        _fileConfig = fileConfig.Value;
    }

    /// <summary>
    /// 文件上传
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [Route("/api/file/upload")]
    public async Task<UploadedOutputDto> UploadAsync(IFormFile file, [FromServices] IWebHostEnvironment environment)
    {
        //文件夹规则
        var dicFolderRules = $"{DateTime.Now:yyyy-MM-dd}";

        if (file.Length <= 0)
            throw new UserFriendlyException("请选择文件上传");

        //是否超过大小
        var isOverLimitFileSize = Convert.ToDecimal(file.Length / 1024) > _fileConfig.SizeLimit;
        if (isOverLimitFileSize)
            throw new UserFriendlyException($"附件大小超过{_fileConfig.SizeLimit}M的限制");
        //是否允许的文件后缀名
        var allowedExtensions = _fileConfig.FileAllowExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
        var ext = Path.GetExtension(file.FileName);
        var isAllowedExtensions = allowedExtensions.All(y => string.Equals(ext, y, StringComparison.CurrentCultureIgnoreCase));
        if (isAllowedExtensions)
            throw new UserFriendlyException("上传文件格式错误");

        //获取文件源名称
        var fileName = Path.GetFileName(file.FileName);

        //原文件夹路径
        var originalFaceDataDirectory = Path.Combine(_fileConfig.IsUploadWebRoot ? environment.WebRootPath : _fileConfig.PhysicalPath, _fileConfig.FileUploadPath, $"{dicFolderRules}");
        var folderPath = Path.Combine(!string.IsNullOrWhiteSpace(_fileConfig.FileUploadPath) ? _fileConfig.FileUploadPath : environment.WebRootPath, originalFaceDataDirectory);
        if (!Directory.Exists(folderPath))
            Directory.CreateDirectory(folderPath);

        //原文件存放全路径
        var fileId = Guid.NewGuid();
        var newFileName = $"{fileId}{ext}";
        var fileFullPath = Path.Combine(folderPath, newFileName);

        //保存原图文件
        await using (var stream = new FileStream(fileFullPath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        //文件上传上传之后的路径
        var fileUploadedPath = $"{Path.Combine(_fileConfig.FileUploadPath, dicFolderRules, newFileName).Replace("\\", "/")}";

        //上传之后的文件对象
        var uploadedDataDto = new UploadedOutputDto
        {
            Id = fileId,
            FileSize = file.Length,
            FileName = fileName,
            FileExtension = ext,
            FileUploadedName = fileName,
            FileUploadedPath = fileUploadedPath,
            FileUploadedFullPath = $"{_fileConfig.DomainHost}{fileUploadedPath}"
        };

        _logger.LogInformation($"文件上传成功：{JsonConvert.SerializeObject(uploadedDataDto)}");

        return uploadedDataDto;
    }

    /// <summary>
    /// 文件上传
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [Route("/api/file/upload-from-stream")]
    public async Task<UploadedOutputDto> UploadFromStreamAsync(string fileName, [FromServices] IWebHostEnvironment environment)
    {
        if (string.IsNullOrWhiteSpace(fileName))
            throw new UserFriendlyException("请传入文件名字");

        _logger.LogInformation($"获取到即将上传的文件为:{fileName}");

        var stream = _contextAccessor.HttpContext?.Request.Body;

        var uploadedDataDto = await UploadFromStreamAsync(fileName, stream, environment);

        return uploadedDataDto;
    }

    /// <summary>
    ///  文件流上传
    /// </summary>
    /// <param name="fileName">文件</param>
    /// <param name="streamInput">流</param>
    /// <param name="environment">环境变量</param>
    /// <returns></returns>
    /// <exception cref="UserFriendlyException"></exception>
    [RemoteService(IsEnabled = false)]
    public async Task<UploadedOutputDto> UploadFromStreamAsync(string fileName, Stream streamInput, [FromServices] IWebHostEnvironment environment)
    {
        //文件夹规则
        var dicFolderRules = $"{DateTime.Now:yyyy-MM-dd}";
        //获取原上传文件夹
        var originalFaceDataDirectory = Path.Combine(_fileConfig.IsUploadWebRoot ? environment.WebRootPath : _fileConfig.PhysicalPath, _fileConfig.FileUploadPath, $"{dicFolderRules}");
        var folderPath = Path.Combine(!string.IsNullOrWhiteSpace(_fileConfig.FileUploadPath) ? _fileConfig.FileUploadPath : environment.WebRootPath, originalFaceDataDirectory);
        if (!Directory.Exists(folderPath))
            Directory.CreateDirectory(folderPath);

        var ext = Path.GetExtension(fileName);
        //原文件存放全路径
        var newFileName = $"{Guid.NewGuid()}{ext}";
        var fileFullPath = Path.Combine(folderPath, newFileName);

        //保存原图文件
        await using var stream = new FileStream(fileFullPath, FileMode.Create);

        var bytes = await streamInput.GetAllBytesAsync();

        await stream.WriteAsync(bytes, 0, bytes.Length)!;

        //文件上传之后的路径
        var fileUploadedPath = $"{Path.Combine(_fileConfig.FileUploadPath, dicFolderRules, newFileName).Replace("\\", "/")}";

        //文件信息
        var fileInfo = new FileInfo(fileFullPath);

        if (bytes.Length > 0)
        {
            _logger.LogInformation($"上传文件大小为{fileInfo.Length}");

            //是否超过大小
            var isOverLimitFileSize = Convert.ToDecimal(fileInfo.Length / 1024) > _fileConfig.SizeLimit;
            if (isOverLimitFileSize)
                throw new UserFriendlyException($"附件大小超过{_fileConfig.SizeLimit}M的限制");
            //是否允许的文件后缀名
            var allowedExtensions = _fileConfig.FileAllowExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            var isAllowedExtensions = allowedExtensions.All(y => string.Equals(ext, y, StringComparison.CurrentCultureIgnoreCase));
            if (isAllowedExtensions)
                throw new UserFriendlyException("上传文件格式错误");

            //上传之后的文件对象
            var uploadedDataDto = new UploadedOutputDto
            {
                FileSize = stream.Length,
                FileName = fileName,
                FileExtension = ext,
                Id = Guid.NewGuid(),
                FileUploadedName = fileName,
                FileUploadedPath = fileUploadedPath,
                FileUploadedFullPath = $"{_fileConfig.DomainHost}{fileUploadedPath}"
            };

            _logger.LogInformation($"文件上传成功：{JsonConvert.SerializeObject(uploadedDataDto)}");

            return uploadedDataDto;
        }

        _logger.LogInformation("文件上传文件大小为0");

        return new UploadedOutputDto();
    }


    /// <summary>
    ///  文件数组上传
    /// </summary>
    /// <param name="fileName">文件</param>
    /// <param name="fileBytes">文件字节数组</param>
    /// <param name="environment">环境变量</param>
    /// <returns></returns>
    /// <exception cref="UserFriendlyException"></exception>
    [RemoteService(IsEnabled = false)]
    public async Task<UploadedOutputDto> UploadFromByteAsync(string fileName, byte[] fileBytes, [FromServices] IWebHostEnvironment environment)
    {
        //文件夹规则
        var dicFolderRules = $"{DateTime.Now:yyyy-MM-dd}";
        //获取原上传文件夹
        var originalFaceDataDirectory = Path.Combine(_fileConfig.IsUploadWebRoot ? environment.WebRootPath : _fileConfig.PhysicalPath, _fileConfig.FileUploadPath, $"{dicFolderRules}");
        var folderPath = Path.Combine(!string.IsNullOrWhiteSpace(_fileConfig.FileUploadPath) ? _fileConfig.FileUploadPath : environment.WebRootPath, originalFaceDataDirectory);
        if (!Directory.Exists(folderPath))
            Directory.CreateDirectory(folderPath);

        var ext = Path.GetExtension(fileName);
        //原文件存放全路径
        var newFileName = $"{Guid.NewGuid()}{ext}";
        var fileFullPath = Path.Combine(folderPath, newFileName);

        //保存原图文件
        await using var stream = new FileStream(fileFullPath, FileMode.Create);

        await stream.WriteAsync(fileBytes, 0, fileBytes.Length)!;

        //文件上传之后的路径
        var fileUploadedPath = $"{Path.Combine(_fileConfig.FileUploadPath, dicFolderRules, newFileName).Replace("\\", "/")}";

        //文件信息
        var fileInfo = new FileInfo(fileFullPath);

        if (fileBytes.Length > 0)
        {
            _logger.LogInformation($"上传文件大小为{fileInfo.Length}");

            //是否超过大小
            var isOverLimitFileSize = Convert.ToDecimal(fileInfo.Length / 1024) > _fileConfig.SizeLimit;
            if (isOverLimitFileSize)
                throw new UserFriendlyException($"附件大小超过{_fileConfig.SizeLimit}M的限制");
            //是否允许的文件后缀名
            var allowedExtensions = _fileConfig.FileAllowExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            var isAllowedExtensions = allowedExtensions.All(y => string.Equals(ext, y, StringComparison.CurrentCultureIgnoreCase));
            if (isAllowedExtensions)
                throw new UserFriendlyException("上传文件格式错误");

            //上传之后的文件对象
            var uploadedDataDto = new UploadedOutputDto
            {
                FileSize = stream.Length,
                FileName = fileName,
                FileExtension = ext,
                Id = Guid.NewGuid(),
                FileUploadedName = fileName,
                FileUploadedPath = fileUploadedPath,
                FileUploadedFullPath = $"{_fileConfig.DomainHost}{fileUploadedPath}"
            };

            _logger.LogInformation($"文件上传成功：{JsonConvert.SerializeObject(uploadedDataDto)}");

            return uploadedDataDto;
        }

        _logger.LogInformation("文件上传文件大小为0");

        return new UploadedOutputDto();
    }
}